package com.syp.auth.service.base.impl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaBuilder.In;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.syp.auth.dao.po.AppEntity;
import com.syp.auth.dao.po.ResourceEntity;
import com.syp.auth.dao.repository.AppRepository;
import com.syp.auth.dao.repository.ResourceRepository;
import com.syp.auth.dao.repository.RoleResourceRepository;
import com.syp.auth.domain.enums.ResourceStatusEnum;
import com.syp.auth.domain.enums.ResourceTypeEnum;
import com.syp.auth.domain.vo.ResourceVo;
import com.syp.auth.domain.vo.easyui.TreeNode;
import com.syp.auth.service.base.ResourceServiceI;

@Service("resourceService")
public class ResourceServiceImpl implements ResourceServiceI {

	@Autowired
	private ResourceRepository resourceRepository = null;
	@Autowired
	private RoleResourceRepository roleResourceRepository = null;
	@Autowired
	private AppRepository appRepository = null;

	@Transactional(rollbackOn = { RuntimeException.class })
	@Override
	public void add(ResourceVo resource) {
		ResourceEntity resourceEntity = new ResourceEntity();

		resourceEntity.setAppId(resource.getAppId());
		if (resource.getAppId() != null) {
			AppEntity app = appRepository.findOne(resource.getAppId());
			resourceEntity.setAppCode(app.getAppCode());
		}
		if (resource.getResPid() != null) {
			resourceEntity.setResPid(resource.getResPid()); // 上级资源
		}
		if (!StringUtils.isEmpty(resource.getResCode())) {
			resourceEntity.setResCode(resource.getResCode()); // 资源代码
		} else {
			resourceEntity.setResCode(UUID.randomUUID().toString());
		}

		resourceEntity.setResName(resource.getResName()); // 资源名称
		resourceEntity.setResUrl(resource.getResUrl()); // 资源路径
		resourceEntity.setIsDefault(resource.getIsDefault());// 作为首页
		resourceEntity.setIsLogAccess(resource.getIsLogAccess());
		resourceEntity.setResSeq(resource.getResSeq()); // 资源排序
		resourceEntity.setResType(resource.getResType()); // 资源类型
		resourceEntity.setResIcon(resource.getResIcon()); // 资源图标
		resourceEntity.setDescription(resource.getResDesc()); // 备注
		resourceEntity.setStatus(resource.getResStatus()); // 资源状态
		resourceEntity.setLastUpdate(Calendar.getInstance().getTime());
		resourceEntity.setCreateTime(Calendar.getInstance().getTime());

		this.resourceRepository.save(resourceEntity);
	}

	@Transactional(rollbackOn = { RuntimeException.class })
	@Override
	public void delete(Long id) {
		List<ResourceEntity> children = this.children(id, null, null);
		if (children != null && !children.isEmpty()) {
			for (ResourceEntity rs : children) {
				this.delete(rs.getId());
			}
		}
		// 删除角色对该资源的引用
		this.roleResourceRepository.deleteByResId(id);
		// 删除该资源
		this.resourceRepository.delete(id);
	}

	public List<ResourceEntity> children(final Long pid, final List<Long> limitIds, final ResourceTypeEnum resType) {
		Specification<ResourceEntity> spec = new Specification<ResourceEntity>() {

			@Override
			public Predicate toPredicate(Root<ResourceEntity> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
				List<Predicate> pList = new ArrayList<>();
				if (resType != null && resType.getType() != null) {
					pList.add(cb.equal(root.get("resType"), resType.getType()));
				}
				if (limitIds != null && !limitIds.isEmpty()) {
					if (pid != null) {
						In<Long> inLimitIds = cb.in(root.get("id").as(Long.class));
						for (Long lid : limitIds) {
							inLimitIds.value(lid);
						}
						pList.add(inLimitIds);
						pList.add(cb.equal(root.get("resPid"), pid));
					} else {
						In<Long> inLimitIds = cb.in(root.get("id").as(Long.class));
						for (Long lid : limitIds) {
							inLimitIds.value(lid);
						}
						pList.add(inLimitIds);
						In<Long> resPidimitIds = cb.in(root.get("resPid").as(Long.class));
						for (Long lid : limitIds) {
							resPidimitIds.value(lid);
						}
						pList.add(cb.or(cb.isNull(root.get("resPid")), resPidimitIds.not()));
					}

				} else {
					if (pid != null) {
						pList.add(cb.equal(root.get("resPid"), pid));
					} else {
						pList.add(cb.isNull(root.get("resPid")));
					}
				}
				return cb.and(pList.toArray(new Predicate[pList.size()]));
			}
		};

		Sort sort = new Sort(new Sort.Order(Sort.Direction.ASC, "resSeq"), new Sort.Order(Sort.Direction.DESC, "id"));
		return this.resourceRepository.findAll(spec, sort);
	}

	public long childrenCount(final Long pid, final List<Long> limitIds, final ResourceTypeEnum resType) {
		Specification<ResourceEntity> spec = new Specification<ResourceEntity>() {

			@Override
			public Predicate toPredicate(Root<ResourceEntity> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
				List<Predicate> pList = new ArrayList<>();
				if (resType != null && resType.getType() != null) {
					pList.add(cb.equal(root.get("resType"), resType.getType()));
				}
				if (limitIds != null && !limitIds.isEmpty()) {
					if (pid != null) {
						In<Long> inLimitIds = cb.in(root.get("id").as(Long.class));
						for (Long lid : limitIds) {
							inLimitIds.value(lid);
						}
						pList.add(inLimitIds);
						pList.add(cb.equal(root.get("resPid"), pid));
					} else {
						In<Long> inLimitIds = cb.in(root.get("id").as(Long.class));
						for (Long lid : limitIds) {
							inLimitIds.value(lid);
						}
						pList.add(inLimitIds);
						In<Long> resPidimitIds = cb.in(root.get("resPid").as(Long.class));
						for (Long lid : limitIds) {
							resPidimitIds.value(lid);
						}
						pList.add(cb.or(cb.isNull(root.get("resPid")), resPidimitIds.not()));
					}

				} else {
					if (pid != null) {
						pList.add(cb.equal(root.get("resPid"), pid));
					} else {
						pList.add(cb.isNull(root.get("resPid")));
					}
				}
				return cb.and(pList.toArray(new Predicate[pList.size()]));
			}
		};

		return this.resourceRepository.count(spec);
	}

	@Transactional(rollbackOn = { RuntimeException.class })
	@Override
	public void edit(ResourceVo resource) {
		ResourceEntity target = this.resourceRepository.findOne(resource.getId());// 当前节点

		target.setAppId(resource.getAppId());
		if (resource.getAppId() != null) {
			AppEntity app = appRepository.findOne(resource.getAppId());
			target.setAppCode(app.getAppCode());
		}
		target.setResPid(resource.getResPid()); // 上级资源
		target.setResCode(resource.getResCode()); // 资源代码
		target.setResName(resource.getResName()); // 资源名称
		target.setResUrl(resource.getResUrl()); // 资源路径
		target.setIsDefault(resource.getIsDefault());// 作为首页
		target.setIsLogAccess(resource.getIsLogAccess());
		target.setResSeq(resource.getResSeq()); // 资源排序
		target.setResType(resource.getResType()); // 资源类型
		target.setResIcon(resource.getResIcon()); // 资源图标
		target.setDescription(resource.getResDesc()); // 备注
		target.setStatus(resource.getResStatus()); // 资源状态
		target.setLastUpdate(Calendar.getInstance().getTime());

		if (resource.getResPid() != null) {
			ResourceEntity parent = this.resourceRepository.findOne(resource.getResPid());
			this.exchParentChild(target, parent);
		}
		this.resourceRepository.save(target);
	}

	/**
	 * 判断是否是将当前节点修改到当前节点的子节点
	 * 
	 * @param self
	 *            当前节点
	 * @param parent
	 *            要修改到的节点
	 * @return
	 */
	private void exchParentChild(ResourceEntity self, ResourceEntity parent) {
		if (parent != null && parent.getResPid() != null) {
			if (parent.getResPid().equals(self.getId())) {
				parent.setResPid(null);
			} else {
				ResourceEntity grand = this.resourceRepository.findOne(parent.getResPid());
				this.exchParentChild(self, grand);
			}
		}
	}

	@Override
	public ResourceVo get(Long id) {
		ResourceVo resource = null;
		ResourceEntity resourceEntity = this.resourceRepository.findOne(id);

		if (resourceEntity != null) {
			resource = new ResourceVo();
			resource.setId(resourceEntity.getId());// ID
			if (resourceEntity.getAppId() != null) {
				AppEntity app = this.appRepository.findOne(resourceEntity.getAppId());
				resource.setAppId(app.getId());
				resource.setAppCode(app.getAppCode());
				resource.setAppName(app.getAppName());
			}
			resource.setResPid(resourceEntity.getResPid()); // 上级资源
			resource.setResCode(resourceEntity.getResCode()); // 资源代码
			resource.setResName(resourceEntity.getResName()); // 资源名称
			resource.setResUrl(resourceEntity.getResUrl()); // 资源路径
			resource.setIsDefault(resourceEntity.getIsDefault());// 作为首页
			resource.setIsLogAccess(resourceEntity.getIsLogAccess());
			resource.setResSeq(resourceEntity.getResSeq()); // 资源排序
			resource.setResType(resourceEntity.getResType()); // 资源类型
			resource.setResIcon(resourceEntity.getResIcon()); // 资源图标
			resource.setResDesc(resourceEntity.getDescription()); // 备注
			if (resourceEntity.getResPid() != null) {
				ResourceEntity parent = this.resourceRepository.findOne(resourceEntity.getResPid());
				resource.setResPid(parent.getId());
				resource.setResPcode(parent.getResCode());
				resource.setResPname(parent.getResName());
			}
			if (resourceEntity.getStatus() != null) {
				ResourceStatusEnum rs = ResourceStatusEnum.getEnum(resourceEntity.getStatus());
				resource.setResStatus(rs.getStatus()); // 资源状态
				resource.setResStatusText(rs.getDesc());
			}
			resource.setIconCls(resource.getResIcon() == null ? "" : resource.getResIcon());
		}
		return resource;
	}

	@Override
	public boolean checkResNameUnique(String resCode) {
		boolean unique = false;

		ResourceEntity resource = this.resourceRepository.getByResCode(resCode);
		if (resource == null) {
			unique = true;
		}
		return unique;
	}

	@Override
	public TreeNode menuTree(TreeNode root, List<Long> limitResIdList, boolean recursive) {
		return this.tree(root, limitResIdList, ResourceTypeEnum.MENU, recursive);
	}

	@Override
	public TreeNode tree(TreeNode root, List<Long> limitIds, ResourceTypeEnum resType, boolean recursive) {
		if (root == null) {
			root = new TreeNode();
		}
		List<ResourceEntity> children = this.children(root.getId(), limitIds, resType);
		if (children != null && !children.isEmpty()) {
			for (ResourceEntity item : children) {
				TreeNode node = new TreeNode();
				node.setId(item.getId());
				node.setText(item.getResName());
				node.setIconCls(StringUtils.isEmpty(item.getResIcon()) ? "" : item.getResIcon());
				String state = this.getTreeNodeState(item.getId(), limitIds, resType);
				node.setState(state);
				Map<String, Object> attributes = new HashMap<String, Object>();
				attributes.put("url", item.getResUrl());
				node.setAttributes(attributes);
				if (recursive) {
					root.getChildren().add(this.tree(node, limitIds, resType, recursive));
				} else {
					root.getChildren().add(node);
				}

			}
		}

		return root;
	}

	@Override
	public ResourceVo treegrid(ResourceVo root, List<Long> limitIds, ResourceTypeEnum resType, boolean recursive) {
		if (root == null) {
			root = new ResourceVo();
		}
		List<ResourceEntity> children = this.children(root.getId(), limitIds, resType);

		if (children != null && !children.isEmpty()) {
			for (ResourceEntity item : children) {
				ResourceVo child = new ResourceVo();

				child.setId(item.getId());// ID
				if (item.getAppId() != null) {
					AppEntity app = this.appRepository.findOne(item.getAppId());
					child.setAppId(app.getId());
					child.setAppCode(app.getAppCode());
					child.setAppName(app.getAppName());
				}
				child.setResPid(item.getResPid()); // 上级资源
				child.setResCode(item.getResCode()); // 资源代码
				child.setResName(item.getResName()); // 资源名称
				child.setResUrl(item.getResUrl()); // 资源路径
				child.setIsDefault(item.getIsDefault());// 作为首页
				child.setIsLogAccess(item.getIsLogAccess());
				child.setResSeq(item.getResSeq()); // 资源排序
				child.setResType(item.getResType()); // 资源类型
				child.setResIcon(item.getResIcon()); // 资源图标
				child.setResDesc(item.getDescription()); // 备注
				child.setResStatus(item.getStatus()); // 资源状态

				if (child.getResPid() != null) {
					ResourceEntity parent = this.resourceRepository.findOne(child.getResPid());
					child.setResPid(parent.getId());
					child.setResPcode(parent.getResCode());
					child.setResPname(parent.getResName());
				}

				child.setResStatusText(ResourceStatusEnum.getEnum(item.getStatus()).getDesc());
				child.setIconCls(StringUtils.isEmpty(child.getResIcon()) ? "" : child.getResIcon());
				String state = this.getTreeNodeState(item.getId(), limitIds, resType);
				child.setState(state);

				if (recursive) {
					root.getChildren().add(this.treegrid(root, limitIds, resType, recursive));
				} else {
					root.getChildren().add(child);
				}
			}
		}
		return root;
	}

	/**
	 * 节点状态
	 * 
	 * @param pid
	 * @param excludeIds
	 * @param resType
	 * @return
	 */
	private String getTreeNodeState(Long pid, List<Long> limitIds, ResourceTypeEnum resType) {
		String state = "";
		Long count = this.childrenCount(pid, limitIds, resType);
		if (count > 0) {
			state = "closed";
		} else {
			state = "open";
		}
		return state;
	}
}